GDK-Win32/4.0: Improve GL on Windows
authorChun-wei Fan <fanchunwei@src.gnome.org>
Fri, 14 Oct 2016 11:04:49 +0000 (19:04 +0800)
committerChun-wei Fan <fanchunwei@src.gnome.org>
Fri, 28 Oct 2016 07:56:49 +0000 (15:56 +0800)
Update the GDKGL implementation:

-Allow legacy contexts to be created.
-Use finer-grained attributes to ask for a pixel format when possible,
 which also adds support for anti-aliasing

In fact the changes here are required for GTKGL to work properly on
Windows for 4.x.

Note that creation of gles contexts is not done here, as the system does
not support such contexts directly on Windows, but only through means such
as ANGLE, which is a totally different issue here.

https://bugzilla.gnome.org/show_bug.cgi?id=773528

gdk/win32/gdkdisplay-win32.h
gdk/win32/gdkglcontext-win32.c

index 009b380d16badac41db30a6efb18b689880fe2fc..50423b319f218b3871ea18ffd4882263d7bc4578 100644 (file)
@@ -47,6 +47,8 @@ struct _GdkWin32Display
   guint hasWglARBCreateContext : 1;
   guint hasWglEXTSwapControl : 1;
   guint hasWglOMLSyncControl : 1;
+  guint hasWglARBPixelFormat : 1;
+  guint hasWglARBmultisample : 1;
 };
 
 struct _GdkWin32DisplayClass
index 02684c7bceca3a823d3dd46db427ade55106a391..a153b9b913ea618d6a40de7b5b5b1e4d9157c763 100644 (file)
@@ -277,20 +277,98 @@ _get_dummy_window_hwnd (GdkWGLDummy *dummy)
 }
 
 static gint
-_get_wgl_pfd (HDC hdc,
-              PIXELFORMATDESCRIPTOR *pfd)
+_gdk_init_dummy_context (GdkWGLDummy *dummy);
+
+#define PIXEL_ATTRIBUTES 17
+
+static gint
+_get_wgl_pfd (HDC                    hdc,
+              PIXELFORMATDESCRIPTOR *pfd,
+              GdkWin32Display       *display)
 {
   gint best_pf = 0;
 
   pfd->nSize = sizeof (PIXELFORMATDESCRIPTOR);
-  pfd->nVersion = 1;
-  pfd->dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER;
-  pfd->iPixelType = PFD_TYPE_RGBA;
-  pfd->cColorBits = GetDeviceCaps (hdc, BITSPIXEL);
-  pfd->cAlphaBits = 8;
-  pfd->dwLayerMask = PFD_MAIN_PLANE;
 
-  best_pf = ChoosePixelFormat (hdc, pfd);
+  if (display != NULL && display->hasWglARBPixelFormat)
+    {
+      GdkWGLDummy dummy;
+      UINT num_formats;
+      gint colorbits = GetDeviceCaps (hdc, BITSPIXEL);
+      guint extra_fields = 1;
+      gint i = 0;
+      int pixelAttribs[PIXEL_ATTRIBUTES];
+
+      if (display->hasWglARBmultisample)
+        {
+          /* 2 pairs of values needed for multisampling/AA support */
+          extra_fields += 2 * 2;
+        }
+
+      /* Update PIXEL_ATTRIBUTES above if any groups are added here! */
+      /* one group contains a value pair for both pixelAttribs and pixelAttribsNoAlpha */
+      pixelAttribs[i] = WGL_DRAW_TO_WINDOW_ARB;
+      pixelAttribs[i++] = GL_TRUE;
+
+      pixelAttribs[i++] = WGL_SUPPORT_OPENGL_ARB;
+      pixelAttribs[i++] = GL_TRUE;
+
+      pixelAttribs[i++] = WGL_DOUBLE_BUFFER_ARB;
+      pixelAttribs[i++] = GL_TRUE;
+
+      pixelAttribs[i++] = WGL_ACCELERATION_ARB;
+      pixelAttribs[i++] = WGL_FULL_ACCELERATION_ARB;
+
+      pixelAttribs[i++] = WGL_PIXEL_TYPE_ARB;
+      pixelAttribs[i++] = WGL_TYPE_RGBA_ARB;
+
+      pixelAttribs[i++] = WGL_COLOR_BITS_ARB;
+      pixelAttribs[i++] = colorbits;
+
+      /* end of "Update PIXEL_ATTRIBUTES above if any groups are added here!" */
+
+      if (display->hasWglARBmultisample)
+        {
+          pixelAttribs[i++] = WGL_SAMPLE_BUFFERS_ARB;
+          pixelAttribs[i++] = 1;
+
+          pixelAttribs[i++] = WGL_SAMPLES_ARB;
+          pixelAttribs[i++] = 8;
+        }
+
+      pixelAttribs[i++] = 0; /* end of pixelAttribs */
+
+      memset (&dummy, 0, sizeof (GdkWGLDummy));
+
+      /* acquire and cache dummy Window (HWND & HDC) and
+       * dummy GL Context, we need it for wglChoosePixelFormatARB()
+       */
+      best_pf = _gdk_init_dummy_context (&dummy);
+
+      if (best_pf == 0 || !wglMakeCurrent (dummy.hdc, dummy.hglrc))
+        return 0;
+
+      wglChoosePixelFormatARB (hdc,
+                               pixelAttribs,
+                               NULL,
+                               1,
+                               &best_pf,
+                               &num_formats);
+
+      wglMakeCurrent (NULL, NULL);
+      _destroy_dummy_gl_context (dummy);
+    }
+  else
+    {
+      pfd->nVersion = 1;
+      pfd->dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER;
+      pfd->iPixelType = PFD_TYPE_RGBA;
+      pfd->cColorBits = GetDeviceCaps (hdc, BITSPIXEL);
+      pfd->cAlphaBits = 8;
+      pfd->dwLayerMask = PFD_MAIN_PLANE;
+
+      best_pf = ChoosePixelFormat (hdc, pfd);
+    }
 
   return best_pf;
 }
@@ -310,7 +388,7 @@ _gdk_init_dummy_context (GdkWGLDummy *dummy)
   dummy->hdc = GetDC (dummy->hwnd);
   memset (&pfd, 0, sizeof (PIXELFORMATDESCRIPTOR));
 
-  best_idx = _get_wgl_pfd (dummy->hdc, &pfd);
+  best_idx = _get_wgl_pfd (dummy->hdc, &pfd, NULL);
 
   if (best_idx != 0)
     set_pixel_format_result = SetPixelFormat (dummy->hdc,
@@ -359,20 +437,28 @@ _gdk_win32_display_init_gl (GdkDisplay *display)
     epoxy_has_wgl_extension (dummy.hdc, "WGL_EXT_swap_control");
   display_win32->hasWglOMLSyncControl =
     epoxy_has_wgl_extension (dummy.hdc, "WGL_OML_sync_control");
+  display_win32->hasWglARBPixelFormat =
+    epoxy_has_wgl_extension (dummy.hdc, "WGL_ARB_pixel_format");
+  display_win32->hasWglARBmultisample =
+    epoxy_has_wgl_extension (dummy.hdc, "WGL_ARB_multisample");
 
   GDK_NOTE (OPENGL,
             g_print ("WGL API version %d.%d found\n"
                      " - Vendor: %s\n"
                      " - Checked extensions:\n"
+                     "\t* WGL_ARB_pixel_format: %s\n",
                      "\t* WGL_ARB_create_context: %s\n"
                      "\t* WGL_EXT_swap_control: %s\n"
                      "\t* WGL_OML_sync_control: %s\n",
+                     "\t* WGL_ARB_multisample: %s\n",
                      display_win32->gl_version / 10,
                      display_win32->gl_version % 10,
                      glGetString (GL_VENDOR),
+                     display_win32->hasWglARBPixelFormat ? "yes" : "no",
                      display_win32->hasWglARBCreateContext ? "yes" : "no",
                      display_win32->hasWglEXTSwapControl ? "yes" : "no",
-                     display_win32->hasWglOMLSyncControl ? "yes" : "no"));
+                     display_win32->hasWglOMLSyncControl ? "yes" : "no",
+                     display_win32->hasWglARBmultisample ? "yes" : "no"));
 
   wglMakeCurrent (NULL, NULL);
   _destroy_dummy_gl_context (dummy);
@@ -380,32 +466,54 @@ _gdk_win32_display_init_gl (GdkDisplay *display)
   return TRUE;
 }
 
+/* Setup the legacy context after creating it */
+static gboolean
+_ensure_legacy_gl_context (HDC           hdc,
+                           HGLRC         hglrc_legacy,
+                           GdkGLContext *share)
+{
+  GdkWin32GLContext *context_win32;
+
+  if (!wglMakeCurrent (hdc, hglrc_legacy))
+    return FALSE;
+
+  if (share != NULL)
+    {
+      context_win32 = GDK_WIN32_GL_CONTEXT (share);
+
+      return wglShareLists (hglrc_legacy, context_win32->hglrc);
+    }
+
+  return TRUE;
+}
+
 static HGLRC
-_create_gl_context (HDC hdc,
-                    GdkGLContext *share,
-                    int flags,
-                    int major,
-                    int minor)
+_create_gl_context_with_attribs (HDC           hdc,
+                                 HGLRC         hglrc_base,
+                                 GdkGLContext *share,
+                                 int           flags,
+                                 int           major,
+                                 int           minor,
+                                 gboolean     *is_legacy)
 {
-  /* we still need a legacy WGL context first for all cases */
-  HGLRC hglrc_base;
-  /* This is the actual WGL context that we want */
   HGLRC hglrc;
   GdkWin32GLContext *context_win32;
 
-  gint attribs[] = {
-    WGL_CONTEXT_PROFILE_MASK_ARB,  WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
-    WGL_CONTEXT_MAJOR_VERSION_ARB, major,
-    WGL_CONTEXT_MINOR_VERSION_ARB, minor,
-    WGL_CONTEXT_FLAGS_ARB, flags,
+  /* if we have wglCreateContextAttribsARB(), create a
+  * context with the compatibility profile if a legacy
+  * context is requested, or when we go into fallback mode
+  */
+  int profile = *is_legacy ? WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB :
+                             WGL_CONTEXT_CORE_PROFILE_BIT_ARB;
+
+  int attribs[] = {
+    WGL_CONTEXT_PROFILE_MASK_ARB,   profile,
+    WGL_CONTEXT_MAJOR_VERSION_ARB, *is_legacy ? 3 : major,
+    WGL_CONTEXT_MINOR_VERSION_ARB, *is_legacy ? 0 : minor,
+    WGL_CONTEXT_FLAGS_ARB,          flags,
     0
   };
 
-  hglrc_base = wglCreateContext (hdc);
-
-  if (!wglMakeCurrent (hdc, hglrc_base))
-    return NULL;
-
   if (share != NULL)
     context_win32 = GDK_WIN32_GL_CONTEXT (share);
 
@@ -413,15 +521,96 @@ _create_gl_context (HDC hdc,
                                       share != NULL ? context_win32->hglrc : NULL,
                                       attribs);
 
-  wglMakeCurrent (NULL, NULL);
-  wglDeleteContext (hglrc_base);
-
   return hglrc;
 }
 
+static HGLRC
+_create_gl_context (HDC           hdc,
+                    GdkGLContext *share,
+                    int           flags,
+                    int           major,
+                    int           minor,
+                    gboolean     *is_legacy,
+                    gboolean      hasWglARBCreateContext)
+{
+  /* We need a legacy context for *all* cases */
+  HGLRC hglrc_base = wglCreateContext (hdc);
+  gboolean success = TRUE;
+
+  /* if we have no wglCreateContextAttribsARB(), return the legacy context when all is set */
+  if (*is_legacy && !hasWglARBCreateContext)
+    {
+      if (_ensure_legacy_gl_context (hdc, hglrc_base, share))
+        return hglrc_base;
+
+      success = FALSE;
+      goto gl_fail;
+    }
+  else
+    {
+      HGLRC hglrc;
+
+      if (!wglMakeCurrent (hdc, hglrc_base))
+        {
+          success = FALSE;
+          goto gl_fail;
+        }
+
+      hglrc = _create_gl_context_with_attribs (hdc,
+                                               hglrc_base,
+                                               share,
+                                               flags,
+                                               major,
+                                               minor,
+                                               is_legacy);
+
+      /* return the legacy context we have if it could be setup properly, in case the 3.0+ context creation failed */
+      if (hglrc == NULL)
+        {
+          if (!(*is_legacy))
+            {
+              /* If we aren't using a legacy context in the beginning, try again with a compatibility profile 3.0 context */
+              hglrc = _create_gl_context_with_attribs (hdc,
+                                                       hglrc_base,
+                                                       share,
+                                                       flags,
+                                                       0, 0,
+                                                       is_legacy);
+
+              *is_legacy = TRUE;
+            }
+
+          if (hglrc == NULL)
+            {
+              if (!_ensure_legacy_gl_context (hdc, hglrc_base, share))
+                success = FALSE;
+            }
+
+          if (success)
+            GDK_NOTE (OPENGL, g_print ("Using legacy context as fallback\n"));
+        }
+
+gl_fail:
+      if (!success || hglrc != NULL)
+        {
+          wglMakeCurrent (NULL, NULL);
+          wglDeleteContext (hglrc_base);
+        }
+
+      if (!success)
+        return NULL;
+
+      if (hglrc != NULL)
+        return hglrc;
+
+      return hglrc_base;
+  }
+}
+
 static gboolean
-_set_pixformat_for_hdc (HDC   hdc,
-                        gint *best_idx)
+_set_pixformat_for_hdc (HDC              hdc,
+                        gint            *best_idx,
+                        GdkWin32Display *display)
 {
   PIXELFORMATDESCRIPTOR pfd;
   gboolean set_pixel_format_result = FALSE;
@@ -429,13 +618,15 @@ _set_pixformat_for_hdc (HDC   hdc,
   /* one is only allowed to call SetPixelFormat(), and so ChoosePixelFormat()
    * one single time per window HDC
    */
-  *best_idx = _get_wgl_pfd (hdc, &pfd);
+  *best_idx = _get_wgl_pfd (hdc, &pfd, display);
+
   if (*best_idx != 0)
     set_pixel_format_result = SetPixelFormat (hdc, *best_idx, &pfd);
 
   /* ChoosePixelFormat() or SetPixelFormat() failed, bail out */
   if (*best_idx == 0 || !set_pixel_format_result)
     return FALSE;
+
   return TRUE;
 }
 
@@ -449,7 +640,7 @@ _gdk_win32_gl_context_realize (GdkGLContext *context,
   /* These are the real WGL context items that we will want to use later */
   HGLRC hglrc;
   gint pixel_format;
-  gboolean debug_bit, compat_bit;
+  gboolean debug_bit, compat_bit, legacy_bit;
 
   /* request flags and specific versions for core (3.2+) WGL context */
   gint flags = 0;
@@ -458,9 +649,11 @@ _gdk_win32_gl_context_realize (GdkGLContext *context,
 
   GdkWindow *window = gdk_gl_context_get_window (context);
   GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
+  GdkWin32Display *win32_display = GDK_WIN32_DISPLAY (gdk_window_get_display (window));
 
   if (!_set_pixformat_for_hdc (context_win32->gl_hdc,
-                               &pixel_format))
+                               &pixel_format,
+                               win32_display))
     {
       g_set_error_literal (error, GDK_GL_ERROR,
                            GDK_GL_ERROR_UNSUPPORTED_FORMAT,
@@ -472,22 +665,39 @@ _gdk_win32_gl_context_realize (GdkGLContext *context,
   debug_bit = gdk_gl_context_get_debug_enabled (context);
   compat_bit = gdk_gl_context_get_forward_compatible (context);
 
+  /* if there isn't wglCreateContextAttribsARB(), or if GDK_GL_LEGACY is set, we default to a legacy context */
+  legacy_bit = !win32_display->hasWglARBCreateContext ||
+               g_getenv ("GDK_GL_LEGACY") != NULL;
+
+  /*
+   * A legacy context cannot be shared with core profile ones, so this means we
+   * must stick to a legacy context if the shared context is a legacy context
+   */
+  if (share != NULL && gdk_gl_context_is_legacy (share))
+    legacy_bit = TRUE;
+
   if (debug_bit)
     flags |= WGL_CONTEXT_DEBUG_BIT_ARB;
   if (compat_bit)
     flags |= WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
 
   GDK_NOTE (OPENGL,
-            g_print ("Creating core WGL context (version:%d.%d, debug:%s, forward:%s)\n",
-            glver_major, glver_minor,
-            debug_bit ? "yes" : "no",
-            compat_bit ? "yes" : "no"));
+            g_print ("Creating %s WGL context (version:%d.%d, debug:%s, forward:%s, legacy: %s)\n",
+                      compat_bit ? "core" : "compat",
+                      glver_major,
+                      glver_minor,
+                      debug_bit ? "yes" : "no",
+                      compat_bit ? "yes" : "no",
+                      legacy_bit ? "yes" : "no"));
 
   hglrc = _create_gl_context (context_win32->gl_hdc,
                               share,
                               flags,
                               glver_major,
-                              glver_minor);
+                              glver_minor,
+                              &legacy_bit,
+                              win32_display->hasWglARBCreateContext);
+
   if (hglrc == NULL)
     {
       g_set_error_literal (error, GDK_GL_ERROR,
@@ -514,6 +724,9 @@ _gdk_win32_gl_context_realize (GdkGLContext *context,
   if (impl->suppress_layered == 1)
     _gdk_win32_window_update_style_bits (window);
 
+  /* Ensure that any other context is created with a legacy bit set */
+  gdk_gl_context_set_is_legacy (context, legacy_bit);
+
   return TRUE;
 }
 
@@ -539,17 +752,6 @@ _gdk_win32_window_create_gl_context (GdkWindow *window,
       return NULL;
     }
 
-  /* We first check whether we have WGL_ARB_create_context... */
-  if (!display_win32->hasWglARBCreateContext)
-    {
-      g_set_error_literal (error, GDK_GL_ERROR,
-                           GDK_GL_ERROR_UNSUPPORTED_PROFILE,
-                           _("The WGL_ARB_create_context extension "
-                             "needed to create core profiles is not "
-                             "available"));
-      return NULL;
-    }
-
   hwnd = GDK_WINDOW_HWND (window);
   hdc = GetDC (hwnd);